package cn.mastercom.sssvr.util;

import java.awt.Font;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Optional;
import java.util.Stack;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.swing.Icon;
import javax.swing.JPanel;
import javax.swing.UIDefaults;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
import javax.swing.plaf.FontUIResource;

import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.DistributedFileSystem;
import org.apache.log4j.PropertyConfigurator;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;

import cn.mastercom.bigdata.util.FilterSparkLogCallBack;
import cn.mastercom.bigdata.util.SshHelper;
import cn.mastercom.bigdata.util.hadoop.hdfs.DirDownloadAndUploadTask;
import cn.mastercom.bigdata.util.hadoop.hdfs.DownloadAndUploadTask;
import cn.mastercom.bigdata.util.hadoop.hdfs.DownloadAndUploadTask.TaskType;
import cn.mastercom.bigdata.util.hadoop.hdfs.HadoopFSOperations;
import cn.mastercom.common.util.Tuple2;
import cn.mastercom.gui.components.BatchFileName;
import cn.mastercom.gui.components.CustomDialog;
import cn.mastercom.gui.hdfsExplorer.views.GlassPane;
import lombok.Getter;
import lombok.extern.log4j.Log4j;

@Log4j
public class HdfsExplorerHelper extends Observable {

    /**
     * 系统图标的缓存
     */
    private static final Map<String, Icon> ICON_MAP = new HashMap<>();
    /**
     * hdfs操作对象
     */
    @Getter
    private HadoopFSOperations hdfs;
    /**
     * 当前导航栏的路径
     */
    private String navigationPath;
    /**
     * 后台任务列表
     */
    private HashMap<Integer, DownloadAndUploadTask> taskMap;
    /**
     * 后台任务线程池
     */
    private ExecutorService backgroundTaskPool;
    /**
     * 后台提交任务线程池
     */
    private ExecutorService submitTaskPool;
    /**
     * 用于保存从hdfs上获取的数据，这样，每次渲染表格视图或树形视图不用重新再取一次数据
     */
    private FileStatus[] fileList;
    /**
     * 命令管理器，用于处理 undo 和 redo 操作
     */
    private CommandManager cmdManager;
    /**
     * ssh操作对象
     */
    private SshHelper ssh /* = new SshHelper(NameNodeIp, RootUser, RootPass, 22) */;
    //private SSHHelper ssh;

    /**
     * loading 面板，在界面繁忙的时候显示
     */
    private JPanel loadingPane;
    private String hdfsUrlList;
    private String hdfsAddr;
    private String rootUser;
    private String nameNodeIp;
    private String rootPass;
    private String jarAddress;
    private String sparkAddress;
    private FilterSparkLogCallBack writeLog = new FilterSparkLogCallBack();

    private HdfsExplorerHelper() {
        this.navigationPath = "";
        this.backgroundTaskPool = Executors.newFixedThreadPool(Math.min(Runtime.getRuntime().availableProcessors(), 8));
        this.submitTaskPool = Executors.newSingleThreadExecutor();
        this.taskMap = new HashMap<>(150000);    // 给15万个槽位
        this.fileList = null;
        this.cmdManager = new CommandManager();
        // 读取配置信息
        System.setProperty("javax.xml.parsers.DocumentBuilderFactory", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");  
        readConfigInfo();
        System.setProperty("HADOOP_USER_NAME", rootUser);
        try {
            hdfs = new HadoopFSOperations(hdfsUrlList);
        } catch (Exception e) {
            log.error(e);
        }
        hdfsAddr = hdfs.getHADOOP_URL();

        PropertyConfigurator.configure(this.getClass().getResourceAsStream("/conf/log4jHE.properties"));
        log.info("Loaded log4j module finished. ");
    }


    public HdfsExplorerHelper(GlassPane loadingPane) {
        this();
        this.loadingPane = loadingPane;
    }

    private void readConfigInfo() {
        try (  InputStream is = this.getClass().getResourceAsStream("/conf/config.xml")) {
            // XMLWriter writer = null;// 声明写XML的对象
            SAXReader reader = new SAXReader();

            OutputFormat format = OutputFormat.createPrettyPrint();
            format.setEncoding("GBK");// 设置XML文件的编码格式


            Document doc = reader.read(is);// 读取XML文件

            {
                List<?> list = doc.selectNodes("//comm/HdfsRoot");
                for (Object aList : list) {
                    System.out.println(list);
                    Element element = (Element) aList;
                    hdfsUrlList = element.getText();
                   
                }
            }
            {
                List<?> list = doc.selectNodes("//comm/SparkAddress");
                for (Object aList : list) {
                    System.out.println(list);
                    Element element = (Element) aList;
                    sparkAddress = element.getText();
                   
                }
            }
            {
                List<?> list = doc.selectNodes("//comm/JarAddress");
                for (Object aList : list) {
                    System.out.println(list);
                    Element element = (Element) aList;
                    jarAddress = element.getText();
                   
                }
            }


            {
                List<?> list = doc.selectNodes("//comm/RootUser");
                for (Object aList : list) {
                    System.out.println(list);
                    Element element = (Element) aList;
                    rootUser = element.getText();
                  
                }
            }

            {
                List<?> list = doc.selectNodes("//comm/NameNodeIp");
                for (Object aList : list) {
                    System.out.println(list);
                    Element element = (Element) aList;
                    nameNodeIp = element.getText();
                   
                }
            }

            {
                List<?> list = doc.selectNodes("//comm/RootPass");
                System.out.println(list);
                Iterator<?> iter = list.iterator();
                try {
                    while (iter.hasNext()) {
                        Element element = (Element) iter.next();
                        if (element != null)
                            rootPass = element.getText();
                        break;
                    }
                } catch (Exception e) {
                    log.error(e);
                }
            }
        } catch (Exception e) {
            log.error(e);
        }
    }


    public String getNavigationPath() {
        return navigationPath;
    }

    public CommandManager getCmdManager() {
        return cmdManager;
    }

    public HashMap<Integer, DownloadAndUploadTask> getTaskMap() {
        return taskMap;
    }

    public String getHdfsAddr() {
        return hdfsAddr;
    }

    public SshHelper getSsh() {
        return ssh;
    }

    public String getRootUser() {
        return rootUser;
    }

    public void setLoadingPane(JPanel loadingPane) {
        this.loadingPane = loadingPane;
    }

    /**
     * 获取系统图标
     */
    public Icon getSystemIcon(String fileName, boolean isDir) {
        if (isDir) {
            Icon ret = ICON_MAP.get("dir");
            if (ret == null) {
                File file = new File(UUID.randomUUID().toString());
                file.mkdir();
                ICON_MAP.put("dir", FileSystemView.getFileSystemView().getSystemIcon(file));
                if (!file.delete()) {
                    log.info("file delete failed");
                  }
            }
            return ICON_MAP.get("dir");
        }
        if (fileName == null || fileName.length() == 0) {
            return null;
        }

        String ext = (!fileName.contains(".") || fileName.lastIndexOf('.') == fileName.length() - 1) ? "unknown"
                : fileName.substring(fileName.lastIndexOf('.') + 1);
        if (ext.contains(":") || ext.contains("/")) ext = "unknown";
        try {
            Icon ret = ICON_MAP.get(ext);
            if (ret == null) {
                File file = File.createTempFile("icon", "." + ext);
                ICON_MAP.put(ext, FileSystemView.getFileSystemView().getSystemIcon(file));
                if (!file.delete()) {
                    log.info("file delete failed");
                  }

            }
            return ICON_MAP.get(ext);
        } catch (IOException e) {
            log.info("获取系统图标失败，失败原因:" + e.getMessage());
            return null;
        }
    }

    /**
     * 初始化样式
     */
    public void initLookAndFeel() {
        try {
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
            // 是否使用粗体
            UIManager.put("swing.boldMetal", Boolean.FALSE);

            UIDefaults uId = UIManager.getDefaults();
            FontUIResource font = new FontUIResource("宋体", Font.PLAIN, 13);// 13
            uId.put("Button.font", font);
            uId.put("TextField.font", font);
            uId.put("Label.font", font);
            uId.put("TextArea.font", font);
            uId.put("TableHeader.font", font);
            uId.put("Table.font", font);
            uId.put("Tree.font", font);
            uId.put("TabbedPane.font", font);
            uId.put("Menu.font", font);
            uId.put("ComboBox.font", font);
        } catch (final Exception e) {
            log.info("发生了异常，异常原因：" + e.getMessage());
        }
    }

    /**
     * 预览文件
     */
    public void previewFile(String filePath) {
        try {
            this.loadingPane.setVisible(true);    // 阻止鼠标事件

            String str = hdfs.viewFileFromHdfs(filePath, 500 * 1024);// 预览500K
            //若文件为空，则设为“ ”
            try {
                //得到整齐的输出
                int inew = str.lastIndexOf('\n');
                str = str.substring(0, inew);
            } catch (Exception e) {
                str = " ";
            }

            //	TxtWindow showByTxt = new TxtWindow();
            String[] linestr = str.split("\n");  //按行分割存储到数组中

            //需要自动检测分割符
            String separator = null;  //每行的分割符

            if (linestr[0].contains("\t")) {
                separator = "\t";
            } else if (linestr[0].contains(",")) {
                separator = ",";
            } else if (linestr[0].contains(" ")) {
                separator = " ";
            } else {
                separator = "\t";
            }

            //求分割后每行的长度
            String[] result = linestr[0].split(separator);


            //列名
            final String[] columnNames = new String[result.length];
            for (int i = 0; i < result.length; i++) {
                columnNames[i] = "c" + i;
            }

            //	String[] line = str.split("\n");

            //把字符串存储到二维数组中


            Object[][] rowData = new Object[linestr.length][result.length];
            for (int l = 0; l < linestr.length; l++) {
                String[] newlinestr = linestr[l].split(separator);
                for (int r = 0; r < result.length; r++)
                    //使用0填充空缺的值，使每行的值个数相同
                    try {
                        rowData[l][r] = newlinestr[r];
                    } catch (Exception e) {
                        rowData[l][r] = 0;
                    }
            }

            txtWindowTab showByTxt = new txtWindowTab(columnNames, rowData, linestr);
            showByTxt.setShowFile(str);
            showByTxt.setVisible(true);
        } finally {
            this.loadingPane.setVisible(false);
        }
    }


    /**
     * 刷新表格信息
     */
    public FileStatus[] listStatus(String path) {
        if (path == null || path.equals("")) {
            return new FileStatus[0];
        }
        if (path.equals(navigationPath)) {
            return fileList;
        }
        fileList = hdfs.listStatus(path);
        navigationPath = path;

        return fileList;
    }

    /**
     * 获取文件大小
     */
    public long getFileSize(Path path) {
        if (path == null) {
            return -1;
        }
        return hdfs.getFileLen(path);
    }

    /**
     * 通知被观察者
     */
    public void notification(final Object arg) {
        this.loadingPane.setVisible(true); // 设置界面为加载中
        this.setChanged(); // 设置允许通知被观察者
        new Thread(() -> {
            try {
                notifyObservers(arg); // 通知所有被观察者更新数据
                if (arg instanceof String)
                    navigationPath = (String) arg; // 更新完数据之后才能重新设置当前的路径
            } catch (Exception e) {
                log.error(e);
            } finally {
                loadingPane.setVisible(false); // 无论有无异常，界面都需回复正常
            }
        }).start();
    }

    /**
     * 合并文件
     */
    public void concat(FileStatus[] files) {
        List<FileStatus> fileStatuses = filterFileForConcat(files);
        List<Tuple2<String, String[]>> tuple2s = groupFile(fileStatuses);
        for (Tuple2<String, String[]> t : tuple2s) {
            concat(t._1(), t._2());
        }
        CustomDialog.show("合并完成");
    }

    /**
     * 将符合条件的文件进行分组，以满足2GB这个限制条件
     */
    private List<Tuple2<String, String[]>> groupFile(List<FileStatus> files) {
        List<Tuple2<String, String[]>> ret = new ArrayList<>();
        List<FileStatus> list = new ArrayList<>();
        long cntFileSize = 0L;
        int i = 0;
        long twoGb = 2147483648L; //=2*1024*1024*1024，2GB
        while (i < files.size()) {
            FileStatus f = files.get(i);
            // 如果超过了 2 GB
            if ((cntFileSize + f.getLen()) > twoGb) {
                if (list.size() >= 2) {
                    ret.add(transformList2Tuple(list));
                }
                list.clear();
                cntFileSize = 0L;
            }
            cntFileSize += f.getLen();
            list.add(f);
            i++;
        }
        if (list.size() >= 2) {
            ret.add(transformList2Tuple(list));
        }
        return ret;
    }

    /**
     * 将列表的第一个元素放到元组一的位置，将其余元素放到元组二的位置
     */
    private Tuple2<String, String[]> transformList2Tuple(List<FileStatus> list) {
        String[] otherFilePaths = new String[list.size() - 1];
        for (int j = 0, len = otherFilePaths.length; j < len; j++) {
            otherFilePaths[j] = list.get(j + 1).getPath().toString();
        }
        return new Tuple2<>(list.get(0).getPath().toString(), otherFilePaths);
    }

    /**
     * 过滤掉不符合合并条件的文件，且已经按文件大小从小到大排序完毕
     */
    private List<FileStatus> filterFileForConcat(FileStatus[] files) {
        List<FileStatus> ret = new LinkedList<>();
        // 用来保存 存在某个后缀名的文件是否需要合并
        Map<String, Boolean> concatWithSuffixNameMatcher = new HashMap<>();
        long twoGb = 2147483648L; // 2GB，2*1024*1024*1024
        for (FileStatus f : files) {
            final String fileName = f.getPath().getName();
            if (f.isDirectory() ||
                    f.getLen() >= twoGb ||
                    fileName.contains("_SUCCESS")) continue;

            /**
             * 如果有后缀名，如 .gitignore a.txt 等需要询问用户是否合并；
             * 诸如 abc. . 等文件无需搭理；
             * 如果发现后缀名超过16位的，则认为这个不是不后缀
             */
            if (fileName.matches(".*\\..+")) {
                String[] split = fileName.split("\\.");
                String suffixName = "." + split[split.length - 1];
                if (suffixName.length() > 16) continue;
                Boolean needConcat = concatWithSuffixNameMatcher.get(suffixName);
                if (needConcat == null) {
                    needConcat = CustomDialog.show("发现后缀名为【" + suffixName + "】的文件，是否合并", "【注意】");
                    if (CustomDialog.show("是否为以后的文件执行相同操作", "【注意】")) {
                        concatWithSuffixNameMatcher.put(suffixName, needConcat);
                    }
                }
                if (!needConcat) continue;
            }
            if (f.getLen() == 0) {
                deleteFileSilent(f.getPath().toString());
                continue;
            }
            ret.add(f);
        }
        ret.sort((o1, o2) -> (int) (o1.getLen() - o2.getLen()));
        return ret;
    }

    /**
     * 合并文件
     */
    public void concat(final String savedFilePath, final String... otherFilePaths) {
        Objects.requireNonNull(savedFilePath);
        Objects.requireNonNull(otherFilePaths);
        if (otherFilePaths.length < 1)
            return;

        Path[] psrcs = new Path[otherFilePaths.length];
        for (int i = 0, len = otherFilePaths.length; i < len; i++) {
            psrcs[i] = new Path(otherFilePaths[i]);
        }
        try {
            Path trg = new Path(savedFilePath);
            hdfs.getDFS().concat(trg, psrcs);
            String msg = "合并完成！已将";
            for (String s : otherFilePaths) {
                msg += "\n\t\t" + s;
            }
            msg += "\n\t合并到\n\t\t" + savedFilePath;
            log.info(msg);
        } catch (IOException e) {
            log.error(e);
        }
    }

    /**
     * 下载 5M 文件
     *
     * @param hdfsPath  hdfs文件路径
     * @param localPath 本地存放路径
     */
    public void down5MFile(final String hdfsPath, final String localPath) {
        submitTaskPool.submit(() -> {
            DownloadAndUploadTask future = null;
			try {
				future = hdfs.read5MFileFromHdfsFuture(hdfsPath, localPath + File.separator + new Path(hdfsPath).getName(), backgroundTaskPool);
			} catch (Exception e) {
				log.info(e);
			}
            taskMap.put(future.hashCode(), future);
        });
    }

    /**
     * @param hdfsPath  hdfs文件/目录的全路径名
     * @param localPath 文件拾取器选中的目录
     */
    public void downFile(final String hdfsPath, final String localPath) {
        submitTaskPool.submit(() -> {
            try {
                DistributedFileSystem dfs = (DistributedFileSystem) HadoopFSOperations.getFs();
                Path path = new Path(hdfsPath);
                long length = dfs.getContentSummary(path).getLength();
                HadoopFSOperations.setDownFileName(path.getName());
                DownloadAndUploadTask task = hdfs.readFileFromHdfsFuture(hdfsPath, localPath + File.separator + path.getName(), length, backgroundTaskPool);
                taskMap.put(task.hashCode(), task);
                hdfs.init_clear();
            } catch (Exception e1) {
                hdfs.init_clear();
                log.info(e1);
            }
        });
    }

    /**
     * 下载文件夹
     *
     * @param hdfsDir   hdfs文件夹路径
     * @param localPath 本地存放路径
     */
    public void downDir(final String hdfsDir, final String localPath) {
        submitTaskPool.submit(() -> {
            try {
                List<DownloadAndUploadTask> tasks = hdfs.readHdfsDirToLocalFuture(hdfsDir, localPath, "",
                        backgroundTaskPool);
                DirDownloadAndUploadTask task = new DirDownloadAndUploadTask(tasks, hdfsDir.replace(hdfsAddr, ""), TaskType.DownloadDir);
                taskMap.put(task.hashCode(), task);
                hdfs.init_clear();
            } catch (Exception e) {
                hdfs.init_clear();
                log.info(e);
                CustomDialog.show("下载过程中出现了一些异常！");
            }
        });
    }

    /**
     * 归并下载
     */
    public void mergeDown(final String hdfsPath, final String localPath) {
        submitTaskPool.submit(() -> {
            try {
                hdfs.getMerge(hdfsPath, localPath, "", true, false);
                CustomDialog.show("归并压缩下载成功！");
                log.info("归并压缩下载成功！");
            } catch (Exception e) {
                CustomDialog.show("归并压缩下载失败！");
                log.info("归并压缩下载失败！失败原因：" + e.getMessage());
            }
        });
    }

    /**
     * 上传文件
     */
    public void uploadFile(final String localFileName, final String hdfsPath) {
        submitTaskPool.submit(() -> {
            DownloadAndUploadTask future = hdfs.copyFileToHdfsFuture(localFileName, hdfsPath, false, backgroundTaskPool);
            taskMap.put(future.hashCode(), future);
        });
    }

    /**
     * 上传文件夹
     */
    public void uploadDir(final String localPath, final String hdfsPath) {
        submitTaskPool.submit(() -> {
            List<DownloadAndUploadTask> tasks = hdfs.copyDirToHdfsFuture(localPath, hdfsPath, false, backgroundTaskPool);
            DirDownloadAndUploadTask task = new DirDownloadAndUploadTask(tasks, localPath, TaskType.UploadDir);
            taskMap.put(task.hashCode(), task);
        });
    }

    /**
     * 解压上传文件夹
     */
    public void uncompressUpload(final String src, final String dest) {
        submitTaskPool.submit(() -> {
            try {
                hdfs.uncompressFileToHDFS(src, dest, false);
                log.info(src + "解压上传成功！");
            } catch (Exception e) {
                log.info(src + "解压上传失败！失败原因：" + e.getMessage());
            }
        });
    }

    /**
     * 归并上传文件
     */
    public void mergeUpload(final String localDirName, final String hdfsPath, final String destFileName) {
        submitTaskPool.submit(() -> {
            try {
                hdfs.putMerge(localDirName, hdfsPath, destFileName, "", false);
                log.info(localDirName + "归并上传成功！");
            } catch (Exception e) {
                log.info(localDirName + "归并上传失败！失败原因：" + e.getMessage());
            }
        });
    }


    /**
     * 重命名
     */
    public void renameFile(String src, String dest) {
        try {
            hdfs.movefile(src, dest);
        } catch (Exception e) {
            CustomDialog.show("重命名失败！");
            log.info("异常原因：" + e.getMessage());
        }
    }

    public void batchRenameFile(FileStatus[] statuses) {
        Optional<Tuple2<String, String>> optionalT2 = BatchFileName.showDialog();
        if (!optionalT2.isPresent()) {
            return;
        }
        String searchStr = optionalT2.get()._1;
        String replaceStr = optionalT2.get()._2;
        for (FileStatus f : statuses) {
            Path target = f.getPath();
            String targetFileName = target.getName();
            if (targetFileName.contains(searchStr)) {
                String newFileName = targetFileName.replace(searchStr, replaceStr);
                String newFilePath = target.getParent().toString() + "/" + newFileName;
                renameFile(target.toString(), newFilePath);
            }
        }
    }

    /**
     * 删除文件
     */
    public void deleteFile(String path) {
        try {
            if (hdfs.delete(path)) {
                log.info(path + "删除成功！！！");
            }
        } catch (Exception e) {
            log.error(path + "删除失败！！！");
        }
    }

    /**
     * 静悄悄地删除文件
     */
    public void deleteFileSilent(String path) {
        try {
            hdfs.delete(path);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    /**
     * 粘贴文件/文件夹
     */
    public void pasteFile(String curpath) {
        // 获取系统剪切板的文本内容[如果系统剪切板复制的内容是文本]
        Transferable t = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); // 跟上面三行代码一样
        try {
            if (null != t && t.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                String tpath = (String) t.getTransferData(DataFlavor.stringFlavor);
                if (tpath.substring(0, 2).equals("cp")) {
                    String[] tpaths = tpath.substring(2).split("\n");
                    for (String targetPath : tpaths) {
                        if (!hdfs.hdfsCopyUtils(targetPath, curpath)) {
                            log.info(targetPath + "复制失败！！！");
                        }
                    }
                } else if (tpath.substring(0, 2).equals("mv")) {
                    String[] tpaths = tpath.substring(2).split("\n");
                    for (String targetPath : tpaths) {
                        if (!hdfs.movefile(targetPath, curpath)) {
                            log.info(targetPath + "剪切失败！！！");
                        }
                    }
                }
            }
        } catch (UnsupportedFlavorException | IOException e) {
            log.info("Error tip: " + e.getMessage());
        }
    }

    /**
     * 添加SUCCESS标志文件
     */
    public void createSuccessFile(String dest) {
        try {
            dest = dest.charAt(dest.length() - 1) == '/' ? dest : dest + "/";
            hdfs.createEmptyFile(dest + "_SUCCESS");
            log.info("添加SUCCESS标志文件成功");
        } catch (Exception e) {
            log.info("添加SUCCESS标志文件失败，失败原因：" + e.getMessage());
        }
    }

    /**
     * 创建文件夹
     */
    public void createDir(String dest) {
        try {
            hdfs.mkdir(dest);
            log.info("创建文件夹" + dest + "成功！");
        } catch (Exception e) {
            log.info("创建文件夹" + dest + "失败，失败原因：" + e.getMessage());
        }
    }

    /**
     * 刷新数据
     */
    public void refresh() {
        try {
            loadingPane.setVisible(true); // 界面设置加载中
            fileList = hdfs.listStatus(navigationPath);
            notification(navigationPath);
        } catch (Exception e) {
            log.info("出现了异常：" + e.getMessage());
        }
    }

    /**
     * 刷新数据
     */
    public void refresh(String dest) {
        navigationPath = dest;
        refresh();
    }

    /**
     * 计算文件大小
     */
    public String getFileLengthDesc(long len) {
        double ret = len;
        if (len < 1024) {
            return ret + "  B";
        } else if ((ret = (len / Math.pow(1024.0, 1))) < 1024) {
            return String.format("%.2f KB", ret);
        } else if ((ret = (len / Math.pow(1024.0, 2))) < 1024) {
            return String.format("%.2f MB", ret);
        } else if ((ret = (len / Math.pow(1024.0, 3))) < 1024) {
            return String.format("%.2f GB", ret);
        } else if ((ret = (len / Math.pow(1024.0, 4))) < 1024) {
            return String.format("%.2f TB", ret);
        } else if ((ret = (len / Math.pow(1024.0, 5))) < 1024) {
            return String.format("%.2f PB", ret);
        } else {
        	ret = (len / Math.pow(1024.0, 6));
            return String.format("%.2f EB", ret );
        }
    }

    /**
     * 获取文件大小，单位为 KB
     *
     * @return double 类型，小数点后保留2位
     */
    public double getFileLengthDescByKB(long len) {
        return Double.parseDouble(String.format("%.2f", len / 1024d));
    }


    /**
     * 更改为777权限
     */
    public void chmod(FileStatus theFile) {
        String permission = theFile.getPermission().toString();
        if (permission.length() == 9 && permission.charAt(7) == '-') {
            if (ssh.Connect()) {
                String sCmd = "hadoop fs -chmod -R 777  " + theFile.getPath().toString() + ";";
                ssh.execCommand(sCmd);
                ssh.Disconnect();
            } else {
                log.info("ssh 未连接");
            }
        }
    }

    /**
     * 取得最后一次修改的时间
     */
    public Date getLastModifedTime(File file) {
        long lastModified = file.lastModified();
        Date date = new Date(lastModified);
        date.setTime(lastModified);
        return date;
    }

    /**
     * 过滤
     *
     * @param filterPath 要过滤文件夹路径
     * @param separator  分离符
     * @param sql        sql语句
     * @param outPath    输出路径
     */
    public void filterSpark(String filterPath, String separator, String sql, String outPath) {
        try {
            String blank = " ";
            String spark_submit = sparkAddress;
            String yarn_conf = "--class cn.mastercom.bigdata.sqlfilter.Main --master yarn";
            String jar_address = jarAddress;
            separator = "\"" + separator + "\"";
            sql = "\"" + sql + "\"";
            String command = spark_submit + blank + yarn_conf + blank + jar_address + blank + filterPath + blank + separator + blank + sql + blank + outPath;
            System.out.println(command);

            log.info("cmd：" + command);
            if (ssh.Connect()) {
                ssh.execCommand(command);
                ssh.Disconnect();
            } else {
                log.info("ssh 未连接");
            }
            log.info("过滤成功");
        } catch (Exception e) {
            CustomDialog.show("过滤失败！");
            log.info("异常原因：" + e.getMessage());
        }
    }

    /**
     * 更改文件为777权限
     */
    public void chmod(String path, boolean isDir) {
        if (!isDir) {
            if (ssh.Connect()) {
                String sCmd = "hadoop fs -chmod -R 777  " + path + ";";
                ssh.execCommand(sCmd);
                ssh.Disconnect();
            } else {
                log.info("ssh 未连接");
            }
        }
    }

    public void executeCmd(ICommand cmd) {
        this.getCmdManager().executeCommand(cmd);
    }

    /**
     * undo
     */
    public void undo() {
        cmdManager.undo();
    }

    /**
     * redo
     */
    public void redo() {
        cmdManager.redo();
    }

    /**
     * 命令接口，用于命令模式
     */
    interface ICommand {
        void execute();

        void undoExecute();

        boolean compareCmd(ICommand cmd);
    }

    /**
     * 命令管理器，用于命令模式
     */
    public class CommandManager {
        private Stack<ICommand> cmdUndoStack = new Stack<ICommand>();
        private Stack<ICommand> cmdRedoStack = new Stack<ICommand>();

        public void executeCommand(ICommand cmd) {
            cmd.execute();
            // 如果 redo 栈为空，则入栈；否则判断要压入的命令是否与之前压入的命令相同，不相同才压栈
            if (cmdUndoStack.isEmpty() || !cmd.compareCmd(cmdUndoStack.peek()))
                cmdUndoStack.push(cmd);
        }

        public void undo() {
            if (!cmdUndoStack.isEmpty()) {
                ICommand cmd = cmdUndoStack.pop();
                cmd.undoExecute();
                cmdRedoStack.push(cmd);
            }
        }

        public void redo() {
            if (!cmdRedoStack.isEmpty()) {
                ICommand cmd = cmdRedoStack.pop();
                cmd.execute();
                cmdUndoStack.push(cmd);
            }
        }
    }

    public static class GotoPathCmd implements ICommand {
        private String nextPath;
        private String previousPath;
        HdfsExplorerHelper helper;

        public GotoPathCmd(String nextPath, HdfsExplorerHelper helper) {
            this.nextPath = nextPath;
            this.previousPath = helper.getNavigationPath();
            this.helper = helper;
        }

        protected String getNextPath() {
            return nextPath;
        }

        protected void setNextPath(String nextPath) {
            this.nextPath = nextPath;
        }

        protected String getPreviousPath() {
            return previousPath;
        }

        protected void setPreviousPath(String previousPath) {
            this.previousPath = previousPath;
        }

        @Override
        public void execute() {
            helper.notification(nextPath);
        }

        @Override
        public void undoExecute() {
            helper.notification(previousPath);
        }

        @Override
        public boolean compareCmd(ICommand cmd) {
            if (!(cmd instanceof GotoPathCmd)) return false;
            GotoPathCmd gotoPathCmd = (GotoPathCmd) cmd;
            return this.getNextPath().equals(gotoPathCmd.getPreviousPath());
        }
    }
}
